#!/usr/bin/env python3
# SPDX-License-Identifier: MulanPSL-2.0+
# Copyright (c) 2020 Huawei Technologies Co., Ltd. All rights reserved.



import os
import sys
import subprocess
import logging
import json
from py_bisect import GitBisect
from perf_range import RangeAdapter, Range
from log_config import logger

BISECT_STEP_GOOD = 0
BISECT_STEP_BAD = 1
BISECT_STEP_ERROR = -1
BISECT_STEP_SKIP = 125

gitbisect = GitBisect()

class GitBisectRun:
    def __init__(self, job_id, metric, perf_range, bad_metric_value):
        self.job_id = job_id
        self.metric = metric
        self.perf_range = perf_range
        self.bad_metric_value = bad_metric_value

    def init_bisect_commit(self):
        """
        Initialize the bisect job content by fetching the latest commit.

        :return: job{}: A dictionary containing the job content with the latest commit
        """
        job = gitbisect.init_job_content(self.job_id)
        logger.debug("Job initialized", job_id=self.job_id)
        try:
            work_dir = os.getcwd()
            commit = subprocess.check_output(
                ['git', '-C', work_dir, 'log', '--pretty=format:%H', '-1'],
                universal_newlines=True
            ).strip()
            if job.get('ss'):
                job['ss']['linux']['commit'] = commit
            else:
                job['upstream_commit'] = commit
        except subprocess.CalledProcessError as e:
            logger.exception("Failed to get commit", error=str(e))
            job['upstream_commit'] = None
        return job

    def bisect_submit_job_get_status(self, job):
        """
        Submit a job and get its status during the bisect process.

        :param job: A dictionary containing job details
        :return: Exits with a specific BISECT_STEP_STATUS based on the job status
        """
        try:
            # 直接调用 submit_wait_job_status 方法
            status = gitbisect.submit_wait_job_status(job, self.metric, self.perf_range, self.bad_metric_value)

            # 根据状态设置退出码
            if not status or status == 'skip':
                sys.exit(BISECT_STEP_SKIP)
            elif status == 'bad':
                sys.exit(BISECT_STEP_BAD)
            elif status == 'good':
                sys.exit(BISECT_STEP_GOOD)
            else:
                logger.error("Unknown status received", status=status)
                sys.exit(BISECT_STEP_ERROR)

        except Exception as e:
            logger.exception("Job processing failed {} {} {}",
                           error=str(e),
                           job_id=job.get('id'),
                           perf_range=str(self.perf_range))
            sys.exit(BISECT_STEP_ERROR)

def main():
    """
    bisect_perf_steps.py 的主函数

    处理命令行参数，初始化bisect环境，并执行性能回归二分查找

    命令行参数:
        1. job_id: 作业ID
        2. metric: 性能指标名称
        3. result_root: 结果根目录
        4. min_val: 性能指标最小正常值
        5. max_val: 性能指标最大正常值
        6. bad_metric_value: 异常的性能指标值

    退出码:
        0: 提交成功且状态为good
        1: 提交成功且状态为bad
        125: 跳过当前提交
        -1: 发生错误
    """
    try:
        result_root = os.path.abspath(sys.argv[3])
        os.makedirs(result_root, exist_ok=True)

        # 初始化日志
        logger.configure(log_dir=result_root)
        logger.context.update({
            'job_id': sys.argv[1],
            'metric': sys.argv[2]
        })

        gitbisect = GitBisect()
        gitbisect.set_log(result_root)  # 必须在其他操作前调用

        # 获取配置参数
        job_id = sys.argv[1]
        metric = sys.argv[2]

        # 解析并验证数值参数
        try:
            min_val = float(sys.argv[4])
            max_val = float(sys.argv[5])
            bad_metric_value = float(sys.argv[6])

            # 必须先初始化日志和数据库连接
        except ValueError as e:
            logger.error("Invalid numeric parameter", error=str(e))
            sys.exit(-1)


        perf_range = Range(min=min_val, max=max_val)

        run = GitBisectRun(job_id, metric, perf_range, bad_metric_value)
        job = run.init_bisect_commit()
        run.bisect_submit_job_get_status(job)

    except Exception as e:
        logger.exception("Bisect execution failed", error=str(e))
        sys.exit(-1)

if __name__ == "__main__":
    main()
